package com.clei.netty.decoder;

import com.clei.utils.ByteUtil;
import com.clei.utils.PrintUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

/**
 * 头尾皆有特殊数据的解码器
 * 主要是为了解决jt808 包头包尾0x7E的粘包问题
 *
 * @author Y
 * @date 2021-12-07
 */
public class HeadTailDecoder extends ByteToMessageDecoder {

    /**
     * 头
     */
    private final byte head;

    /**
     * 尾
     */
    private final byte tail;

    /**
     * 是否解包 是否读取包头包尾
     */
    private final boolean unpack;

    public HeadTailDecoder(byte head, byte tail, boolean unpack) {
        this.head = head;
        this.tail = tail;
        this.unpack = unpack;
    }

    /**
     * decode
     *
     * @param ctx context
     * @param in  ByteBuf
     * @param out out
     * @throws Exception 异常
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        if (null == in) {
            return;
        }
        while (in.isReadable()) {
            in.markReaderIndex();
            int packetBeginIndex = in.readerIndex();
            // 开始
            byte b = in.readByte();
            if (head == b) {
                if (!in.isReadable()) {
                    in.resetReaderIndex();
                    return;
                }
                // 结束
                b = in.readByte();
                while (tail != b) {
                    // 等待包尾
                    if (!in.isReadable()) {
                        in.resetReaderIndex();
                        return;
                    }
                    b = in.readByte();
                }

                int pos = in.readerIndex();
                int packetLength = pos - packetBeginIndex;
                in.resetReaderIndex();
                if (packetLength > 2) {
                    if (this.unpack) {
                        // 这里解包 只读取包头包尾之间的数据
                        in.readByte();
                        ByteBuf res = in.readBytes(packetLength - 2);
                        out.add(res);
                        in.readByte();
                    } else {
                        // 不解包 包头包尾一起读取
                        ByteBuf res = in.readBytes(packetLength);
                        out.add(res);
                    }
                } else {
                    if (2 == packetLength) {
                        if (head == tail) {
                            // 第二个作为包头
                            in.readByte();
                        } else {
                            // 头尾相连了
                            // 等下一个包头
                            in.readBytes(2);
                        }
                    }
                }
            } else {
                // 非以包头开始
                in.resetReaderIndex();
                byte[] data = new byte[in.readableBytes()];
                in.readBytes(data);
                int headIndex = -1;
                boolean haveHead = false;
                for (int i = 0; i < data.length; i++) {
                    if (head == data[i]) {
                        headIndex = i;
                        haveHead = true;
                        break;
                    }
                }
                // 是否有包头 有的话读取到包头
                if (haveHead) {
                    in.resetReaderIndex();
                    in.readBytes(headIndex);
                }
                PrintUtil.log("有包头 ： {}, 错误数据 : {}", haveHead, ByteUtil.toHex(data));
            }
        }
    }
}
